package kernel_gui.gui.extended;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import kernel_gui.gui.generic.graphic.ControlActors;

import common.entities.enums.ACTION;
import common.entities.enums.SEX;
import common.entities.persistent.Actor;
import common.entities.persistent.Movie;

@SuppressWarnings("serial")
public class EControlActors extends ControlActors {

	private String firstNameSubStr = null;
	private String lastNameSubStr = null;
	private SEX selectedSex = null;
	private Movie movie = null;
	private Movie selected_movie = null;
	
	private int count = 0;
	private int numberOfRecordsPerPage;
	private int currentPage = 0;
	private int totalPages = 0;
	
	private List<Actor> currentDataInTableModel;
	private DefaultTableModel currentTableModel = new DefaultTableModel();
	
	private int currentRowBeingUpdated = -1;
	private int currentNewRow = -1;
	
	
	
	/*General*/
	
	public EControlActors() {
		
		/*Command to execute on cancel*/
		setExecuteOnCancel(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				Component components[] = MainPanel.getComponents();
				for (Component c : components) {
					if(c instanceof JPanel){
						JPanel panel = (JPanel) c;
						Component pcomponents[] = panel.getComponents();
						for(Component pc : pcomponents){
							if(pc instanceof JPanel){
								JPanel anotherPanel = (JPanel) pc;
								Component pccomponents[] = anotherPanel.getComponents();
								for(Component pcc : pccomponents){
									pcc.setEnabled(true);
								}
							}
							pc.setEnabled(true);
						}
					}
					c.setEnabled(true);
				}
				ResultTable.setEnabled(true);
				updateButtonsAndLabelsAfterReturnedResultSet();
				setWindowComponentsWhenCalledFromAnotherFrame();
			}
		});
		
		/*Command to execute before long request*/
		setExecuteBeforeLongRequest(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				Component components[] = MainPanel.getComponents();
				for (Component c : components) {
					if(c instanceof JPanel){
						JPanel panel = (JPanel) c;
						Component pcomponents[] = panel.getComponents();
						for(Component pc : pcomponents){
							if(pc instanceof JPanel){
								JPanel anotherPanel = (JPanel) pc;
								Component pccomponents[] = anotherPanel.getComponents();
								for(Component pcc : pccomponents){
									pcc.setEnabled(false);
								}
							}
							pc.setEnabled(false);
						}
					}
					c.setEnabled(false);
				}
				ResultTable.setEnabled(false);
			}
		});
		
		/*Update the numberOfRecords variable*/
		NumOfRecordsInPageComboBox.setSelectedIndex(2);
		numberOfRecordsPerPage = Integer.parseInt(NumOfRecordsInPageComboBox.getSelectedItem().toString());
		
		
		/*Create defaultive model*/
		currentTableModel = new DefaultTableModel(new Object [][] {},new String [] {"First Name", "Last Name", "Sex"});
		ResultTable.setModel(currentTableModel);
		updateButtonsAndLabelsAfterReturnedResultSet();
	}
	


	/*Search Panel*/
	
	
	@Override
	protected void SearchButtonActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			if(getSearchVariablesData()){
				countResultAndPopulateTable(1);
			}
		}

	}
	
	private boolean getSearchVariablesData(){
		
		if(!PartOfFirstNameTextField.getText().equals(""))
			firstNameSubStr = PartOfFirstNameTextField.getText();
		else
			firstNameSubStr = null;
		
		if(!PartOfLastNameTextField.getText().equals(""))
			lastNameSubStr = PartOfLastNameTextField.getText();
		else
			lastNameSubStr = null;
		
		selectedSex = SEX.parseStringToSEX(SexCombobox.getSelectedItem().toString());
		
		movie = selected_movie;
		
		return true;
	}

	protected void countResultAndPopulateTable(int page){
		
		final int pageWhenFinished = page;
		/*Command to execute after returning from*/
		int requestID = executeBeforeLongRequest(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				/*Updated count & labels*/
				EControlActors.this.count = Integer.parseInt(answer.toString());
				EControlActors.this.FoundNumberLabel.setText(numbers_nf.format(EControlActors.this.count));
				
				if(EControlActors.this.count != 0)
					EControlActors.this.currentPage = 1;
				else
					EControlActors.this.currentPage = 0;
				
				EControlActors.this.PageNumberLabel.setText(numbers_nf.format(EControlActors.this.currentPage));
				EControlActors.this.totalPages = (int) Math.ceil(((float)EControlActors.this.count)/numberOfRecordsPerPage);
				EControlActors.this.TotalPageNumberLabel.setText(numbers_nf.format(EControlActors.this.totalPages));
									
				/*Get the data*/
				populateTableWithResults(pageWhenFinished);
				
			}
		});
	
		
		kernel.countActors(firstNameSubStr, lastNameSubStr, selectedSex, movie, requestID);
	}

	private void populateTableWithResults(int page){
		/*Command to execute after returning from*/
		
		final int pageNumberToSet = page;
		int requestID = executeBeforeLongRequest(new CommandToExecute() {
			
			@SuppressWarnings("unchecked")
			@Override
			public void command(Object answer) {
				if(answer != null){
					EControlActors.this.currentDataInTableModel = (List<Actor>) answer;
					
					/*Create new table model, and insert the rows*/
					currentTableModel = new DefaultTableModel(new Object [][] {},new String [] {"First Name", "Last Name", "Sex"});
					/*Add the tableModelListener for handeling table changes (update, insert)*/
					currentTableModel.addTableModelListener(new TableModelListener() {
						
						@Override
						public void tableChanged(TableModelEvent e) {
							tableChangedEventHandler(e);
						}
					});
					
					/*Add the result data to the table model*/
					for(int i = 0; i < EControlActors.this.currentDataInTableModel.size(); i++){
						Actor actor = EControlActors.this.currentDataInTableModel.get(i);
						Object[] rowData = new Object[] {actor.getFirstName(), actor.getLastName(), actor.getSex().toString()};
						currentTableModel.insertRow(i, rowData);
					}
					
					
					/*Set the table model and the first page of it*/
					ResultTable.setModel(currentTableModel);
					if(EControlActors.this.count != 0)
						currentPage = pageNumberToSet;
					else
						currentPage = 0;
					
					updateButtonsAndLabelsAfterReturnedResultSet();
				}
			}
		});
		
		int from = (page <= 0) ? 0 : (page-1)*numberOfRecordsPerPage;
		int to = (page <= 0) ? 0 : page*numberOfRecordsPerPage;
		
		kernel.getActors(firstNameSubStr, lastNameSubStr, selectedSex, movie, from, to, requestID);
	}
	


	@Override
	protected void MovieButtonActionPerformed(ActionEvent evt) {
		openAnotherFrame(EControlMovies.class, false, new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				EControlActors.this.selected_movie = (Movie)answer;
				if(EControlActors.this.selected_movie != null){
					EControlActors.this.MovieNameTextField.setText(EControlActors.this.selected_movie.getTitle() + 
							" (" + EControlActors.this.selected_movie.getReleaseYear() + ")");
				}else{
					EControlActors.this.MovieNameTextField.setText("");
				}
				
			}
		});

	}
	
	@Override
	protected void ClearButtonActionPerformed(ActionEvent evt) {
		PartOfFirstNameTextField.setText("");
		PartOfLastNameTextField.setText("");
		SexCombobox.setSelectedIndex(0);
		MovieNameTextField.setText("");
		selected_movie = null;
	}
	
	

	
	/*Table Panel*/
	
	private void updateButtonsAndLabelsAfterReturnedResultSet(){
		EControlActors.this.PageNumberLabel.setText(String.valueOf(EControlActors.this.currentPage));
		EControlActors.this.totalPages = (int) Math.ceil(((float)EControlActors.this.count)/numberOfRecordsPerPage);
		EControlActors.this.TotalPageNumberLabel.setText(String.valueOf(EControlActors.this.totalPages));
		
		if(totalPages == 0){
			NextButton.setEnabled(false);
			PreviousButton.setEnabled(false);
			NumOfRecordsInPageComboBox.setEnabled(false);
		}else{
			NextButton.setEnabled(true);
			PreviousButton.setEnabled(true);
			NumOfRecordsInPageComboBox.setEnabled(true);
		}	
	}
	
	@Override
	protected void NumOfRecordsInPageComboBoxActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			numberOfRecordsPerPage = Integer.parseInt(NumOfRecordsInPageComboBox.getSelectedItem().toString());
			
			/*fix problem when the user is on page 100 in 5rec-per-page but there is no page 100 in 100rec-per-page*/
			int expectedTotalPages = (int) Math.ceil(((float)EControlActors.this.count)/numberOfRecordsPerPage);
	
			
			if(currentPage > expectedTotalPages)
				currentPage = expectedTotalPages;
			
			populateTableWithResults(currentPage);
		}

	}

	@Override
	protected void PreviousButtonActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			if(currentPage > 1){
				int askingPage = currentPage-1;
				populateTableWithResults(askingPage);
			}else{
				populateTableWithResults(totalPages);
			}
		}

	}

	@Override
	protected void NextButtonActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			if(currentPage < totalPages){
				int askingPage = currentPage+1;
				populateTableWithResults(askingPage);
			}else{
				populateTableWithResults(1);
			}
		}

	}
	
	protected void tableChangedEventHandler(TableModelEvent e){
		if(!isCalledAsLongRequestFromOtherFrame()){
			if(e.getType() == TableModelEvent.UPDATE){/*If this is update*/
				int rownum = e.getFirstRow();
				if(rownum != currentNewRow){
					/*if the row we are changing isn't the currentNewRow*/
					if(currentNewRow != -1){
						/*if we started insert - don't allow it*/
						int userAnswer = JOptionPane.showConfirmDialog(this, "You cannot update while in insert.\n" +
								"Do you wish to commit your previous change?\n" +
								"(pressing no will discard your change)", "", JOptionPane.YES_NO_OPTION);
						switch(userAnswer){
							case JOptionPane.YES_OPTION:
								/*discard current change*/
								undoChangesToRow(rownum);
								/*Updating previous changes*/
								insertNewRecordIntoDB();
								break;
							case JOptionPane.NO_OPTION:
								/*Discarding previous changes*/
								currentTableModel.removeRow(EControlActors.this.currentNewRow);
								InsertButton.setForeground(DeleteButton.getForeground());
								UpdateButton.setForeground(Color.red);
								currentNewRow = -1;
								currentRowBeingUpdated = rownum;
						}
					}else{/*we havent started insert*/
						if(currentRowBeingUpdated == -1){
							currentRowBeingUpdated = rownum;
							UpdateButton.setForeground(Color.red);
						}else if(currentRowBeingUpdated != rownum){
							int userAnswer = JOptionPane.showConfirmDialog(this, "You can only update one row at a time.\n" +
									"Do you wish to commit your previous change?\n" +
									"(pressing no will discard your change)", "", JOptionPane.YES_NO_OPTION);
							switch(userAnswer){
								case JOptionPane.YES_OPTION:
									/*discard current change*/
									undoChangesToRow(rownum);
									/*Updating previous changes*/
									updateChangesInTable();
									break;
								case JOptionPane.NO_OPTION:
									/*Discarding previous changes*/
									undoChangesToRow(currentRowBeingUpdated);
									UpdateButton.setForeground(Color.red);
									currentRowBeingUpdated = rownum;
							}
						}
					}
				
				}	
			}	
		}
	}
	
	private void undoChangesToRow(int rowNum){
		currentTableModel.removeRow(rowNum);
		Actor actor = currentDataInTableModel.get(rowNum);
		currentTableModel.insertRow(rowNum, new Object[] {actor.getFirstName(), actor.getLastName(), actor.getSex()	});
	}
	
	/**
	 * 
	 * @return true if there aren't any unsaved changed, otherwise false
	 */
	private boolean unsavedChangesCheck(int create_update_all){
		if(currentRowBeingUpdated != -1 && create_update_all != 2){
			int userAnswer = JOptionPane.showConfirmDialog(this, "You have unsaved change.\n" +
					"Do you wish to update the record?\n" +
					"(pressing no will discard your change)", "", JOptionPane.YES_NO_OPTION);
			switch(userAnswer){
				case JOptionPane.YES_OPTION:
					/*Updating change*/
					updateChangesInTable();
					return false;
				case JOptionPane.NO_OPTION:
					/*Discarding change*/
					undoChangesToRow(currentRowBeingUpdated);
					UpdateButton.setForeground(DeleteButton.getForeground());
					currentRowBeingUpdated = -1;
					return true;
			}
		}else if(currentNewRow != -1 && create_update_all != 1){
			int userAnswer = JOptionPane.showConfirmDialog(this, "You have unsaved change.\n" +
					"Do you wish to insert the record?\n" +
					"(pressing no will discard your record)", "", JOptionPane.YES_NO_OPTION);
			switch(userAnswer){
				case JOptionPane.YES_OPTION:
					/*Updating change*/
					insertNewRecordIntoDB();
					return false;
				case JOptionPane.NO_OPTION:
					/*Discarding change*/
					currentTableModel.removeRow(EControlActors.this.currentNewRow);
					InsertButton.setForeground(DeleteButton.getForeground());
					currentNewRow = -1;
					return true;
		}
		}
		
		return true;
	}
	
	/*Action Panel*/
	

	@Override
	protected void DeleteButtonActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			int[] selectedRowIndexes = ResultTable.getSelectedRows();
			if(selectedRowIndexes.length != 1){
				JOptionPane.showMessageDialog(this, "Please select ONE record to delete");
			}else{
				/*Command to execute after returning from*/
				int requestID = executeBeforeLongRequest(new CommandToExecute() {
					
					@Override
					public void command(Object answer) {
						if(currentDataInTableModel.size() != 1)
							EControlActors.this.countResultAndPopulateTable(currentPage);
						else
							EControlActors.this.countResultAndPopulateTable(--currentPage);
					}
				});
				
				Actor actor = this.currentDataInTableModel.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0]));
				kernel.actionActor(ACTION.DELETE, actor, requestID);
			}
		}
	}

	@Override
	protected void UpdateButtonActionPerformed(ActionEvent evt) {
		if(currentRowBeingUpdated == -1){
			JOptionPane.showMessageDialog(this, "There are no changes to commit.");
		}else{
			updateChangesInTable();
		}

	}

	@Override
	protected void InsertButtonActionPerformed(ActionEvent evt) {
		if(currentRowBeingUpdated != -1){
			int userAnswer = JOptionPane.showConfirmDialog(this, "You cannot insert while in update.\n" +
					"Do you wish to commit your previous change?\n" +
					"(pressing no will discard your change)", "", JOptionPane.YES_NO_OPTION);
			switch(userAnswer){
				case JOptionPane.YES_OPTION:
					/*discard current change by not creating a new row*/
					/*Updating previous changes*/
					updateChangesInTable();
					return;
				case JOptionPane.NO_OPTION:
					/*Discarding previous changes*/
					undoChangesToRow(currentRowBeingUpdated);
					UpdateButton.setForeground(DeleteButton.getForeground());
					currentRowBeingUpdated = -1;
					break;
			}
		}
		if(unsavedChangesCheck(1)){/*pass insert mode*/
			if(currentNewRow == -1){
				InsertButton.setForeground(Color.red);
				currentTableModel.addRow(new Object[]{"", "", ""});
				currentNewRow = currentTableModel.getRowCount()-1;
				ResultTable.changeSelection(currentNewRow, 1, false, false);
			}else{
				insertNewRecordIntoDB();
			}
		}


	}
	
	private Actor parseDataFromTable(Actor actorToUpdate, int rowNum){
		ResultTable.changeSelection(rowNum, 1, false, false);
		
		String newActorFirstName = currentTableModel.getValueAt(rowNum, 0).toString();
		String newActorLastName = currentTableModel.getValueAt(rowNum, 1).toString();
		SEX newActorSex = SEX.parseStringToSEX(currentTableModel.getValueAt(rowNum, 2).toString());
		if(newActorSex == null){
			JOptionPane.showMessageDialog(this, "Illegal sex, only 'Male' & 'Female' are allowed (not case-sensative)");
			return null;
		}
		
		
		return new Actor(actorToUpdate.getId(), newActorFirstName, newActorLastName, newActorSex);
	}
	
	private void updateChangesInTable(){
		if(unsavedChangesCheck(2)){/*pass update mode*/
			/*Parse changes*/
			ResultTable.changeSelection(currentRowBeingUpdated, 1, false, false);
			
			
			final Actor updatedActor = parseDataFromTable(currentDataInTableModel.get(currentRowBeingUpdated),
					currentRowBeingUpdated);
			if(updatedActor != null){/*If the parsing succeeded*/
				/*Command to execute after returning from*/
				int requestID = executeBeforeLongRequest(new CommandToExecute() {
					
					@Override
					public void command(Object answer) {
						EControlActors.this.currentDataInTableModel.remove(EControlActors.this.currentRowBeingUpdated);
						EControlActors.this.currentDataInTableModel.add(EControlActors.this.currentRowBeingUpdated, updatedActor);
						UpdateButton.setForeground(DeleteButton.getForeground());
						EControlActors.this.currentRowBeingUpdated = -1;
					}
				});
				
				
				kernel.actionActor(ACTION.UPDATE, updatedActor, requestID);
			}
		}
	}
	
	private void insertNewRecordIntoDB(){
		final Actor newActor = parseDataFromTable(new Actor(),
				currentNewRow);
		
		if(newActor != null){/*If the parsing succeeded*/
			/*Command to execute after returning from*/
			int requestID = executeBeforeLongRequest(new CommandToExecute() {
				
				@Override
				public void command(Object answer) {
					currentTableModel.removeRow(EControlActors.this.currentNewRow);
					EControlActors.this.currentNewRow = -1;
					InsertButton.setForeground(DeleteButton.getForeground());
					if(EControlActors.this.currentPage != 0){/*If we are not inserting new records into empty set*/
						countResultAndPopulateTable(EControlActors.this.currentPage);
					}
					
				}
			});
			
			
			kernel.actionActor(ACTION.CREATE, newActor, requestID);
		}
	}
	/*Connections Panel*/
	

	@Override
	protected void ActorMoviesConnectionActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			int[] selectedRowIndexes = ResultTable.getSelectedRows();
			if(selectedRowIndexes.length != 1){
				JOptionPane.showMessageDialog(this, "Please select ONE record in order to see it's connections");
			}else{
				Actor actor = this.currentDataInTableModel.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0]));
				openPreCreatedFrame(new EConnectionsFrame(EConnectionsFrame.CONNECTION_TYPE.Actor,
						actor, EConnectionsFrame.CONNECTION_TYPE.Movie), null);
			}
		}

	}
	
	/*Going back*/
	
	@Override
	protected void BackButtonActionPerformed(ActionEvent evt) {
		if(unsavedChangesCheck(3)){
			if(isCalledAsLongRequestFromOtherFrame()){
				int selectedRowIndx = ResultTable.getSelectedRow();
				if(selectedRowIndx != -1 && currentDataInTableModel != null){
					Actor actorAnswer = currentDataInTableModel.get(ResultTable.convertRowIndexToModel(selectedRowIndx));
					setAnswerToCallingFrame(actorAnswer);
				}
				returnToCallingFrame();
			}else{
				openAnotherFrame(EControlPanelMenuFrame.class, true);
			}
		}
	}

	protected void setWindowComponentsWhenCalledFromAnotherFrame(){
		if(isCalledAsLongRequestFromOtherFrame()){
			MovieButton.setEnabled(false);
			DeleteButton.setEnabled(false);
			UpdateButton.setEnabled(false);
			InsertButton.setEnabled(false);
			ActorMoviesConnection.setEnabled(false);
			BackButton.setText("SELECT");
		}
	}

}
